package com.tangsm.spring.boot.api.util;

import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 自定义占位符替换工具类
 *
 * @author tangsm
 */
public class PlaceholderUtils {

    /**
     * 占位符正则表达式：$+花括号
     * <p>占位符示例：{@code ${xxx}}</p>
     */
    public static final Pattern PATTERN_BRACE = Pattern.compile("\\$\\{(.*?)}");

    /**
     * 占位符正则表达式：$+尖括号
     * <p>占位符示例：{@code $<xxx>}</p>
     */
    public static final Pattern PATTERN_ANGLE = Pattern.compile("\\$<(.*?)>");

    /**
     * 匹配器
     */
    private static Matcher matcher;

    /**
     * 替换字符串占位符，可变参数，字符串中使用{0}{1}...{*}顺序下标，表示占位符
     *
     * @param source 需要匹配的字符串，示例："名字:{0},年龄:{1},学校:{2}"
     * @param params 可变参数，动态参数
     * @return 替换后的字符串
     */
    public static String replaceWithVarargs(String source, Object... params) {
        return MessageFormat.format(source, params);
    }

    /**
     * 替换字符串占位符，Map传参，字符串中使用${key}表示占位符
     *
     * @param source 需要匹配的字符串，示例："名字:${name},年龄:${age},学校:${school}"
     * @param params 参数集,Map类型
     * @return 替换后的字符串
     */
    public static String replaceWithMap(String source, Map<String, Object> params) {
        if (StringUtils.isBlank(source) || CollectionUtils.isEmpty(params)) {
            return source;
        }
        String targetString = source;
        matcher = PATTERN_BRACE.matcher(source);
        while (matcher.find()) {
            try {
                String key = matcher.group();
                //如果占位符是{} 这里就是key.substring(1, key.length() - 1).trim()
                String keyClone = key.substring(2, key.length() - 1).trim();
                Object value = params.get(keyClone);
                if (value != null) {
                    targetString = targetString.replace(key, value.toString());
                }
            } catch (Exception e) {
                throw new RuntimeException("字符串格式化程序失败", e);
            }
        }
        return targetString;
    }

    /**
     * 替换字符串占位符，POJO传参，字符串中使用${key}表示占位符
     *
     * <p>注：利用反射，自动获取对象属性值 (必须有get方法) </p>
     *
     * @param source 需要匹配的字符串
     * @param params 参数实体
     * @return 替换后的字符串
     */
    public static String replaceWithObject(String source, Object params) {
        if (StringUtils.isBlank(source) || ObjectUtils.isEmpty(params)) {
            return source;
        }

        String targetString = source;

        PropertyDescriptor pd;
        Method getMethod;

        // 匹配${}中间的内容 包括括号
        matcher = PATTERN_BRACE.matcher(source);
        while (matcher.find()) {
            String key = matcher.group();
            String holderName = key.substring(2, key.length() - 1).trim();
            try {
                pd = new PropertyDescriptor(holderName, params.getClass());
                // 获得get方法
                getMethod = pd.getReadMethod();
                Object value = getMethod.invoke(params);
                if (value != null) {
                    targetString = targetString.replace(key, value.toString());
                }
            } catch (Exception e) {
                throw new RuntimeException("字符串格式化程序失败", e);
            }
        }
        return targetString;
    }

    /**
     * 获取String中的占位符keys
     * <p>示例：名字:${name},年龄:${age},学校:${school}，返回：Set[name,age,school]</p>
     *
     * @param source  源字符串
     * @param pattern 正则表达式，pattern示例：<pre> {@code
     *                // 尖括号：<placeHolder> 表示为占位符
     *                Pattern pattern = Pattern.compile("\\$<(.*?)>");
     *
     *                // 大括号：{placeHolder} 表示为占位符， 上面的示例中就使用{}作为占位符
     *                Pattern pattern = Pattern.compile("\\$\\{(.*?)}");
     *                } </pre>
     * @return Set集合
     */
    public static Set<String> getPlaceholderKeys(String source, Pattern pattern) {
        Set<String> placeHolderSet = new HashSet<>();

        // 参数有误返回空集合
        if (StringUtils.isBlank(source) || ObjectUtils.isEmpty(pattern)) {
            return placeHolderSet;
        }

        // 根据正则表达式初始匹配器
        matcher = pattern.matcher(source);
        while (matcher.find()) {
            //示例: {name}
            String key = matcher.group();
            //示例： name
            String placeHolder = key.substring(2, key.length() - 1).trim();
            placeHolderSet.add(placeHolder);
        }

        return placeHolderSet;
    }
}
